package com.tianji.learning.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.api.client.user.UserClient;
import com.tianji.api.dto.user.UserDTO;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.Constants.LearningConstants;
import com.tianji.learning.Constants.RedisConstants;
import com.tianji.learning.domain.po.PointsBoard;
import com.tianji.learning.domain.po.PointsBoardSeason;
import com.tianji.learning.domain.query.PointsBoardQuery;
import com.tianji.learning.domain.vo.PointsBoardItemVO;
import com.tianji.learning.domain.vo.PointsBoardVO;
import com.tianji.learning.mapper.PointsBoardMapper;
import com.tianji.learning.service.IPointsBoardSeasonService;
import com.tianji.learning.service.IPointsBoardService;
import com.tianji.learning.utils.TableInfoContext;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static com.tianji.learning.Constants.LearningConstants.POINTS_BOARD_TABLE_PREFIX;

/**
 * <p>
 * 学霸天梯榜 服务实现类
 * </p>
 *
 * @author author
 * @since 2025-04-15
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class PointsBoardServiceImpl extends ServiceImpl<PointsBoardMapper, PointsBoard> implements IPointsBoardService {

    private final StringRedisTemplate redisTemplate;
    private final UserClient userClient;
    private final IPointsBoardSeasonService pointsBoardSeasonService;
    @Override
    public PointsBoardVO queryPointsBoard(PointsBoardQuery query) {
        //1.获取当前登录用户id
        Long userId = UserContext.getUser();
        //2.判断传来的参数是否指定赛季时间
        PointsBoardVO vo = new PointsBoardVO();
        List<PointsBoardItemVO> vos = null;
        //2.1格式化时间为yyyyMM
        DateTimeFormatter yyyyMM = DateTimeFormatter.ofPattern("yyyyMM");
        if (query.getSeason()!=null && query.getSeason()>0){
            //3.如果有，从db中查询指定赛季时间
           /* //3.1根据赛季id查询赛季时间
            PointsBoardSeason season = pointsBoardSeasonService.getById(query.getSeason());

            query.setSeason(Long.valueOf(season.getBeginTime().format(yyyyMM)));
            vos = queryRedisBoard(query);*/
            vos=queryHistoryBoard(query);
        }else {
            //4.如果没有，查询当前赛季时间
            String format = LocalDateTime.now().format(yyyyMM);
            query.setSeason(Long.valueOf(format));
            vos=queryRedisBoard(query);
        }
        //5.查询当前用户积分值
        String key=RedisConstants.POINTS_KEY_PREFIX + query.getSeason();
        Double score = redisTemplate.opsForZSet().score(key, String.valueOf(userId));
        if (score!=null){
            vo.setPoints(score.intValue());
        }
        //查询该用户的排名
        if (score!=null){
            Long l = redisTemplate.opsForZSet().reverseRank(key, String.valueOf(userId));
            vo.setRank(l.intValue()+1);
        }
        vo.setBoardList(vos);
        log.info("查询积分排行榜，参数：{}，返回结果：{}",query,vo);
        return vo;
    }

    @Override
    public void createPointsBoardTableBySeason(PointsBoardSeason season) {
        getBaseMapper().createPointsBoardTable(POINTS_BOARD_TABLE_PREFIX + season.getId());
    }

    private List<PointsBoardItemVO> queryHistoryBoard(PointsBoardQuery query) {
        Long seasonId = query.getSeason();
        log.info("查询积分排行榜，参数：{}",query);
        TableInfoContext.setInfo(LearningConstants.POINTS_BOARD_TABLE_PREFIX+seasonId);
        Page<PointsBoard> page = this.lambdaQuery()
                .page(query.toMpPage("id", true));
        List<PointsBoard> records = page.getRecords();
        if (CollUtil.isEmpty(records)){
            log.info("查询结果为空,暂时还没有人回答");
            return CollUtil.newArrayList();
        }
        //利用stream获取用户id集合
        Set<Long> userIds = records.stream().map(PointsBoard::getUserId).collect(Collectors.toSet());
        //2.根据userIds查询用户信息
        List<UserDTO> users = userClient.queryUserByIds(userIds);
        //将用户信息转换成map
        Map<Long, String> userMap = users.stream().collect(Collectors.toMap(UserDTO::getId, u -> u.getName()));
        List<PointsBoardItemVO> vos= new ArrayList<>(records.size());
        for (PointsBoard record : records) {
            PointsBoardItemVO vo = new PointsBoardItemVO();
            vo.setName(userMap.get(record.getUserId()));
            vo.setPoints(record.getPoints());
            vo.setRank(Math.toIntExact(record.getId()));
            vos.add(vo);
        }
        return vos;
    }

    private List<PointsBoardItemVO> queryRedisBoard(PointsBoardQuery query) {
        String key = RedisConstants.POINTS_KEY_PREFIX + query.getSeason();
        // 1.计算分页
        int from = (query.getPageNo() - 1) * query.getPageSize();
        log.info("分页参数，pageNO:{},pageSize:{}", query.getPageNo(), query.getPageSize());
        //1.根据key 得到number，score,查询前100名榜单
        Set<ZSetOperations.TypedTuple<String>> typedTuples = redisTemplate.opsForZSet().reverseRangeWithScores(key, from,
                /*query.getPageSize()-1*/(long) query.getPageNo() *query.getPageSize()-1);
        log.info("redis查询结果：{}",typedTuples);
        if (CollUtil.isEmpty(typedTuples)){
            return CollUtil.newArrayList();
        }
        //2.获取用户id
        ArrayList<Long> userIds = new ArrayList<>(typedTuples.size());
        for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
            String userIdStr = typedTuple.getValue();
            if (userIdStr!=null){
                Long UserID = Long.valueOf(userIdStr);
                userIds.add(UserID);
            }
        }
        //3.利用远程调用用户服务查询用户信息
        List<UserDTO> users = userClient.queryUserByIds(userIds);
        //4.利用stream流将users转为map<userid,userName>
        Map<Long, String> map = users.stream().collect(Collectors.toMap(UserDTO::getId, UserDTO::getName));
        List<PointsBoardItemVO> vos = new ArrayList<>(typedTuples.size());
        int count=1+from;
        for (ZSetOperations.TypedTuple<String> tuple : typedTuples) {
            PointsBoardItemVO vo = new PointsBoardItemVO();
            String userIdStr = tuple.getValue();
            if (userIdStr!=null){
                //5.根据userId查询用户名
                Long userID = Long.valueOf(userIdStr);
                vo.setName(map.get(userID));
                //6.设置积分值和名次
                Double score = tuple.getScore();
                if (score != null) {
                    vo.setPoints(score.intValue());
                }
                //7.设置名次
                vo.setRank(count);
                vos.add(vo);
                count++;
            }
        }
        return  vos;
    }
}
